﻿using System;
using System.Diagnostics;
using System.IO;
using System.Text;
using Jink.Core.Json;
using Jink.Protocol.Core;
using Jink.Core.Handlers.Bind;
using Jink.Protocol.Core.ErrorCodes;
using Jink.Protocol.Core.StatusCodes;
using XenoGears.Assertions;

namespace Jink.Core
{
    public class CoreResponse : IResponse
    {
        private readonly CoreRequest _request;
        IRequest IResponse.Request { get { return Request; } }
        public CoreRequest Request { get { return _request; } }

        private StatusCode? _statusCode;
        public StatusCode StatusCode
        {
            get { return _statusCode ?? StatusCode.OK; }
            set { _statusCode = value; }
        }

        private ErrorCode? _errorCode;
        public ErrorCode ErrorCode
        {
            get { return _errorCode ?? ErrorCode.Success; }
            set { _errorCode = value; }
        }

        private Exception _exception;
        public Exception Exception
        {
            get { return _exception; }
            set
            {
                _exception = value;

                if (_statusCode == null) _statusCode = _exception.AcquireStatusCode();
                if (_errorCode == null) _errorCode = _exception.AcquireErrorCode();
            }
        }

        private bool _resultHasBeenSet = false;
        private Object _result;
        public Object Result
        {
            get { return _result; }
            set
            {
                _result = value;
                _resultHasBeenSet = true;
            }
        }

#if TRACE
        private StringBuilder Trace { get; set; }
#endif

        public CoreResponse(CoreRequest request)
        {
#if TRACE
            Trace = new StringBuilder();
            var traceListener = new TextWriterTraceListener(new StringWriter(Trace));
            System.Diagnostics.Trace.Listeners.Add(traceListener);

            try
            {
#endif
                try
                {
                    _request = request;
                    var boundHandler = _request.BindHandler();
                    var result = boundHandler();

                    var handlerReturnsSomething = boundHandler.Method.ReturnType != typeof(void);
                    (_resultHasBeenSet && handlerReturnsSomething).AssertFalse();
                    Result = handlerReturnsSomething ? result : new {};
                }
                catch (Exception ex)
                {
                    Exception = ex;
                }
#if TRACE
            }
            finally
            {
                System.Diagnostics.Trace.Listeners.Remove(traceListener);
            }
#endif
        }

        public void DoSend(IResponseMedia media)
        {
            var responseJson = new
            {
                StatusCode = StatusCode,
                ErrorCode = ErrorCode,
                Result = Result,
#if DEBUG
                Exception = Exception, 
#endif

#if TRACE
                // todo. make sure that we trace everything that might be essential :
                // 1) received request 
                // 2) matching modules: XXX... success, YYY... failed -> selected module XXX
                // 3) matching handlers XXX... <mb even bind process> <match result>, YYY... <match result> -> selected handler XXX
                // 4) invoking method... success|success and acquired result|crash
                // 5) sent response
                // todo. also trace information in registries of JSON, handlers and such
                // todo. also add tostrings for everything around in this and other dlls
                Trace = Trace,
#endif
            }.ToJson();

#if DEBUG
            var response = responseJson.ToPrettyString();
            media.ContentEncoding = Encoding.UTF8;
            media.ContentType = "text/html";
            media.Content.Write(response);
            media.StatusCode = (int)StatusCode;
            media.DoComplete();
#else
            var response = responseJson.ToPrettyString();
            media.ContentEncoding = Encoding.UTF8;
            media.ContentType = "application/json";
            media.Content.Write(response);
            media.StatusCode = (int)StatusCode;
            media.DoComplete();
#endif
        }
    }
}
